#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int globalVariables;//这就是一个全局变量。作用域:整个进程(程序)，且可被其他文件访问到。
int i;

//static修饰全局变量，作用域依旧是整个进程，但是不被其他文件访问到(只有在定义的文件内可以访问到)
static int staticGlobalVariables;

int main() {

    //局部变量和全局变量
    //局部变量: 1、定义在{}内的变量 2、作用域在所在的{}内 3、生命周期随{}的结束而结束
    int i = 10;//局部变量
    {
        int i = 100;
        //当变量同名时，以就近原则为准，所以值是100
        printf("局部变量i的值:%d\n", i);
        int j = 100;
    }
    //因为{}内的i的生命周期只在{}内，所以此时的i是外面的i，所以值是10
    printf("局部变量i的值:%d\n", i);
    //因为{}内的j的生命周期只在{}内，所以直接没法使用——会编译报错
//    printf("局部变量j的值:%d\n", j);
    //全局变量: 1、定义在函数外部的变量(所以也称"外部变量") 2、全局变量在定义时如果不初始化，则编译器会设置默认值0——而非局部变量的使用分配空间的原值
    // 3、作用域是整个程序  4、声明周期随程序的结束才结束
    printf("全局变量globalVariables的值:%d\n", globalVariables);
    //不过记住还有就近原则，所以如果全局变量与局部变量命名冲突了，则优先采用局部变量的值——所以尽量不要搞重名
    printf("看似是全局变量i的值实际是局部变量i的值:%d\n", i);

    //static关键字的作用:
    // static修饰的局部变量: 1、作用域在所在的{}内 2、声明周期随程序的结束而结束
    for (int k = 0; k < 4; ++k) {
        int cht1 = 0;
        static int cht2;
        cht1++;
        cht2++;
        //原理:
        // ·普通局部变量在定义时被分配在栈上，栈上的空间在使用结束后会被系统自动回收，下一次继续使用时，则重新定义和赋值
        // ·被static修饰的局部变量定义在静态存储区中，不会随使用结束被系统回收
        // ·修饰的局部变量若没有初始化则自动初始化为0
        printf("测试static修饰变量的声明周期:%d %d\n", cht1, cht2);
    };
    //static作用总结:
    // 1、改变作用域(将全局变量和函数的作用域从进程变成所在文件)。
    // 2、改变声明周期(将局部变量原来随{}的结束而结束变成随进程的结束而结束)
    // TIPS:static可以修饰变量也可以修饰函数

    //const关键字作用: 被const修饰的对象是只读的。
    const int constVariables = 12;
    //语法分析异常: Cannot assign to variable 'constVariables' with const-qualified type 'const int'
//    constVariables = 15;
    printf("被const修饰的变量:%d\n", constVariables);
    int value = 0;
    int value2 = 13;
    const int *p1 = &value;//表示该指针指向一个整型常量，不能通过该指针变量p1改变它指向的值。
    //语法分析异常: Read-only variable is not assignable
//    *p1 = 12;
    //TIPS:是不能通过指针变量本身修改，但是未被修饰的变量本身依旧能够修改。
    value = 12;
    //且该指针可以指向别的常量。
    p1 = &value2;
    int const *p2 = &value;//跟 const int 含义相同
    //    *p2 = 12;
    p2 = &value2;
    int *const p3 = &value;//表示该指针是指向整型的常量指针，它不能再指向别的变量，但可以通过它修改变量的值。
    *p3 = 45;
    //语法分析异常: Cannot assign to variable 'p3' with const-qualified type 'int *const'
//    p3 = &value2;
    const int *const p4;//表示指向整型常量的常量指针，它既不能再指向别的常量，也不能通过它修改指向变量的值。
//    *p4 = 78;
//    p4 = &value2;
    int const *const p5;//与 const int *const 含义相同。
//    *p5 = 78;
//    p5 = &value2;
    //TIPS:如果看得眼花缭乱可以考虑将数据类型去掉后再看规律

    //const和define的区别:
    // 1、就起作用的阶段而言: #define是在编译的预处理阶段起作用，而const是在编译、运行时起作用。
    // 2、就起作用的方式而言: #define只是简单的文本替换，没有类型检查。const有对应的数据类型，是要进行判断的，可以避免一些低级错误。
    // 3、就存储方式而言: #define只是进行展开，有多少地方使用，就替换多少次。它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程只有一份备份。
    // 4、从代码调试的方便程度而言: const常量可以进行调试，define不可以进行调试，因为预编译阶段就已经替换掉了。

    return 0;

}